package jp.dev7.android.util;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

public abstract class AbstractWorker<T> implements Worker<T> {

    @SuppressWarnings("serial")
    protected static class Cancel extends Exception {
    }

    /**
     * 実行開始済み.
     */
    private boolean started = false;

    /**
     * キャンセル済み.
     */
    private boolean canceled = false;

    /**
     * バックグラウンド処理完了済み.
     */
    private boolean completed = false;

    @Override
    public void execute(final Handler handler) {
        Log.d(getClass().toString(), "バックグラウンド処理開始");

        // 一度しか実行できない
        if (started) {
            throw new IllegalStateException("AbstractWorkerは一度しか実行できません.");
        }
        started = true;

        // スレッドを立ててバックグラウンド処理開始
        new Thread() {
            public void run() {

                // 処理結果
                T result = null;

                // 処理中に発生した例外
                Throwable error = null;

                try {

                    checkCancel();

                    result = doInBackground();

                    checkCancel();

                    Log.d(getClass().toString(), "バックグラウンド処理正常終了");
                } catch (Cancel cancel) {
                    canceled = true;
                    Log.d(getClass().toString(), "バックグラウンド処理キャンセル");
                } catch (Throwable e) {
                    error = e;
                    Log.e(getClass().toString(), "バックグラウンド処理中に例外", e);
                } finally {

                    if (error == null) {
                        handler.sendMessage(done(result));
                    } else {
                        handler.sendMessage(catchError(error));
                    }

                    completed = true;
                    Log.d(getClass().toString(), "バックグラウンド処理完了");
                }
            }
        }.start();
    }

    @Override
    public final void cancel() {
        canceled = true;
    }

    @Override
    public final boolean isCanceled() {
        return canceled;
    }

    @Override
    public final boolean isCompleted() {
        return completed;
    }

    /**
     * キャンセル済みの場合、Cancel例外を投げる.
     * 
     * @throws Cancel
     *             -
     */
    protected void checkCancel() throws Cancel {
        if (isCanceled()) {
            throw new Cancel();
        }
    }

    protected abstract T doInBackground() throws Throwable;

    protected abstract Message done(T result);

    protected Message catchError(Throwable error) {
        Message message = new Message();
        Bundle data = new Bundle();
        data.putSerializable(ERROR, error);
        message.setData(data);
        return message;
    }

}
